package com.fluidops.datacatalog.reltables.model;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.openrdf.model.URI;

import com.fluidops.datacatalog.reltables.adapter.ExtractionAdapter;
import com.fluidops.datacatalog.reltables.labelbase.ILabelSource;

public class Dataset{

//	private static final Logger logger = Logger.getLogger(Dataset.class.getName());

	private SortedSet<RelatednessEntry> relatedDatasets = new TreeSet<RelatednessEntry>();

	private double relatednessThreshold;

	private ILabelSource labelSource;

	private URI identifier;


	private ExtractionAdapter entityExtractor;
	
	private Set<Entity> entities;
	private List<URI> dimURIs;
	
	private Set<String> dimStringLabels;
	private Set<String> dimSignalStringLabels;
	
	private Set<String> dimValueStringLabels;
	private Set<String> dimValueSignalStringLabels;
	
	

	public Dataset(URI identifier, ILabelSource labelSource, ExtractionAdapter entityExtractor, 
							double relatednessThreshold){
		this.identifier = identifier;
		this.relatednessThreshold = relatednessThreshold;

		this.labelSource = labelSource;
		this.entityExtractor = entityExtractor;
	}

	public List<URI> getDimensionURIs(){
		if(this.dimURIs == null){
			this.dimURIs = this.entityExtractor.retrieveDimensionURIs(this.identifier);
		}
		return this.dimURIs;
	}
	
	
	public Set<String> dimValueSignalStringLabels(){
		if(this.dimValueSignalStringLabels == null){
			Set<String> tempDimValueLabels = this.entityExtractor.retrieveDimensionValueStringLabels(this.identifier, getDimensionURIs());
			this.dimValueSignalStringLabels = this.labelSource.getLabel(tempDimValueLabels);
		}
		return this.dimValueSignalStringLabels;
	}
	
	public Set<String> dimValueStringLabels(){
		if(this.dimValueStringLabels == null){
			this.dimValueStringLabels = this.entityExtractor.retrieveDimensionValueStringLabels(this.identifier, getDimensionURIs());
		}
		return this.dimValueStringLabels;
	}

	public Set<String> dimStringLabels(){
		if(this.dimStringLabels == null){
			this.dimStringLabels = this.entityExtractor.retrieveDimensionStringLabels(this.identifier, getDimensionURIs());
		}
		return this.dimStringLabels;
	}

	public Set<String> dimSignalStringLabels(){
		if(this.dimSignalStringLabels == null){
			Set<String> tempSet = this.dimStringLabels();
			this.dimSignalStringLabels = this.labelSource.getLabel(tempSet);
		}
		return this.dimSignalStringLabels;
	}

	public Set<Entity> getEntities(){
		if(this.entities == null){
			List<URI> entityURIs = this.entityExtractor.retrieveEntityURIs(this.identifier);
			Entity entity;
			this.entities = new HashSet<Entity>();
			for(URI e : entityURIs){
				Set<String> entityStringLabel = this.entityExtractor.retrieveEntityStringLabels(e);
				entity = Entity.getInstance(e, entityStringLabel, this.labelSource);
				this.entities.add(entity);
			}
		}

		return entities;
	}

	public void clearCaches(){
		this.entities = null;
		this.dimStringLabels = null;
		this.dimValueSignalStringLabels = null;
		this.dimURIs = null;
		this.dimSignalStringLabels = null;
		this.dimValueStringLabels = null;
	}

	public URI getIdentifier(){
		return this.identifier;
	}


	public void addRelatedDataset(Dataset related, final double similarity){
		if(similarity >= this.relatednessThreshold){
			this.relatedDatasets.add(new RelatednessEntry(related.getIdentifier(), similarity));
		}
	}

	public SortedSet<RelatednessEntry> getRelatedDatasets(){
		TreeSet<RelatednessEntry> topK = new TreeSet<RelatednessEntry>();

		for(RelatednessEntry re : ((TreeSet<RelatednessEntry>)this.relatedDatasets).descendingSet() ){
			topK.add(re);
		}

		return topK;
	}


	public boolean hasRelatednessScore(Dataset ds){
		for(RelatednessEntry re : this.relatedDatasets){
			if(re.identifier.equals(ds.identifier)){
				return true;
			}
		}
		return false;
	}

	public void closeConnections(){
		this.entityExtractor.close();
	}
	
	public boolean equals(Object o){
		if(o instanceof Dataset){
			Dataset d = (Dataset) o;
			return this.identifier.equals(d.identifier);
		}
		return false;
	}
	
	public int hashCode(){
		return this.identifier.hashCode();
	}
	
	public void resetRelatedDatasets(){
		this.relatedDatasets = new TreeSet<RelatednessEntry>();
	}
	
	public static class RelatednessEntry implements Comparable<RelatednessEntry>{
		
		private static AtomicInteger numEntry = new AtomicInteger(0);
		
		private URI identifier;
		private double similarity;
		private int id;
		

		private RelatednessEntry(URI identifier, double similarity){
			this.identifier = identifier;
			this.similarity = similarity;
			this.id = numEntry.getAndIncrement();
		}

		@Override
		public int compareTo(RelatednessEntry arg0) {
		
			int res = (int) (this.similarity - arg0.similarity);
			if(res == 0d){
				// older entries are "bigger"
				res = this.id < arg0.id ? 1 : -1;
			}
			return res;
			//return (int) (this.similarity - arg0.similarity);
		}

		@Override
		public int hashCode(){
			return this.identifier.hashCode() * 31;
		}

		@Override
		public boolean equals(Object o){
			if(o instanceof RelatednessEntry){
				RelatednessEntry re = (RelatednessEntry) o;
				return this.identifier.equals(re.identifier);
			}
			return false;
		}
		
		public URI getIdentifier() {
			return identifier;
		}

		public double getSimilarity() {
			return similarity;
		}
		
	}
}
